﻿using System;

namespace Sencha.Serialization.Bson
{
	[TypeSerializer(typeof(Serializers.BsonDateTimeSerializer))]
	public struct BsonDateTime : IEquatable<BsonDateTime>, IComparable, IComparable<BsonDateTime>
	{
		public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

		private long milliseconds;

		public long Raw { get { return this.milliseconds; } }

		public BsonDateTime(long millisecondsAfterEpoch)
		{
			this.milliseconds = millisecondsAfterEpoch;
		}
		public BsonDateTime(DateTime dateTime)
		{
			this.milliseconds = (dateTime - Epoch).Ticks / TimeSpan.TicksPerMillisecond;
		}

		#region IEquatable<BsonDateTime> Members

		public override bool Equals(object obj)
		{
			if (obj is BsonDateTime)
				return this.Equals((BsonDateTime)obj);
			return false;
		}

		public override int GetHashCode()
		{
			return this.milliseconds.GetHashCode();
		}

		public bool Equals(BsonDateTime other)
		{
			return this.milliseconds == other.milliseconds;
		}

		#endregion
		#region IComparable Members

		public int CompareTo(object obj)
		{
			if (obj is BsonDateTime)
				return this.CompareTo((BsonDateTime)obj);

			return 1;
		}

		#endregion
		#region IComparable<BsonDateTime> Members

		public int CompareTo(BsonDateTime other)
		{
			return this.milliseconds.CompareTo(other.milliseconds);
		}

		#endregion

		public static bool operator >(BsonDateTime a, BsonDateTime b)
		{
			return a.CompareTo(b) == 1;
		}
		public static bool operator <(BsonDateTime a, BsonDateTime b)
		{
			return a.CompareTo(b) == -1;
		}
		public static bool operator >=(BsonDateTime a, BsonDateTime b)
		{
			return a.CompareTo(b) != -1;
		}
		public static bool operator <=(BsonDateTime a, BsonDateTime b)
		{
			return a.CompareTo(b) != 1;
		}
		public static bool operator ==(BsonDateTime a, BsonDateTime b)
		{
			return a.milliseconds == b.milliseconds;
		}
		public static bool operator !=(BsonDateTime a, BsonDateTime b)
		{
			return a.milliseconds != b.milliseconds;
		}
		public static implicit operator BsonDateTime(long millisecondsAfterEpoch)
		{
			return new BsonDateTime(millisecondsAfterEpoch);
		}
		public static implicit operator BsonDateTime(DateTime dateTime)
		{
			return new BsonDateTime(dateTime);
		}
		public static explicit operator long(BsonDateTime value)
		{
			return value.milliseconds;
		}
		public static explicit operator DateTime(BsonDateTime value)
		{
			return new DateTime(Epoch.Ticks + value.milliseconds * TimeSpan.TicksPerMillisecond, DateTimeKind.Utc);
		}

		public override string ToString()
		{
			return ((DateTime)this).ToString("yyyy-MM-ddTHH:mm:ss.FFFZ");
		}

	}
}
